#include"ffmuxer.h"


FFMuxer::FFMuxer()
    :headerFlag(false),trailerFlag(false)
{

}


FFMuxer::~FFMuxer()
{
    if(!headerFlag.load(std::memory_order_acquire)){
        writeTrailer();
    }
    if(fmtCtx){
        avformat_close_input(&fmtCtx);
    }
    url.clear();
}

void FFMuxer::init(const std::string &url_)
{
    url = url_;
    initMuxer();
}

void FFMuxer::initMuxer()
{
    int ret  = avformat_alloc_output_context2(&fmtCtx,nullptr,nullptr,url.c_str());
    if(ret < 0){
        std::cerr << "Alloc Output Context Fail !" << std::endl;
        printError(ret);
        return;
    }

    ret = avio_open(&fmtCtx->pb,url.c_str(),AVIO_FLAG_WRITE);
    if(ret < 0){
        std::cerr << "Open File Fail !" << std::endl;
        printError(ret);
        return;
    }
}

void FFMuxer::printError(int ret)
{
    char errorBuffer[AV_ERROR_MAX_STRING_SIZE];
    int res = av_strerror(ret,errorBuffer,sizeof errorBuffer);
    if(res < 0){
        std::cerr << "Unknow Error!" << std::endl;
    }
    else{
        std::cerr << "Error:" << errorBuffer << std::endl;
    }
}

void FFMuxer::addStream(AVCodecContext* codecCtx)
{
    AVStream* stream = avformat_new_stream(fmtCtx,nullptr);
    if(stream == nullptr){
        std::cerr << "New Stream Fail !" << std::endl;
        return;
    }

    int ret = avcodec_parameters_from_context(stream->codecpar,codecCtx);
    if(ret < 0){
        std::cerr << "Copy Parameters From Context Fail !" << std::endl;
        printError(ret);
        return;
    }

    stream->time_base = codecCtx->time_base;
    if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO){
        aCodecCtx = codecCtx;
        aStream = stream;
        aStreamIndex = stream->index;
    }
    else if(codecCtx->codec_type == AVMEDIA_TYPE_VIDEO){
        vCodecCtx = codecCtx;
        vStream = stream;
        vStreamIndex = stream->index;
    }

    streamCount++;
    if(streamCount == 2){
        readyFlag.store(true,std::memory_order_release);
    }
}

int FFMuxer::mux(AVPacket *packet)
{
    std::lock_guard<std::mutex>lock(mutex);
    if(trailerFlag.load(std::memory_order_release)){
        return -1;
    }

    int streamIndex = packet->stream_index;

    AVRational srcTimeBase, dstTimeBase;
    if(streamIndex == aStreamIndex){
        srcTimeBase = aCodecCtx->time_base;
        dstTimeBase = aStream->time_base;
    }
    else if(streamIndex == vStreamIndex){
        srcTimeBase = vCodecCtx->time_base;
        dstTimeBase = vStream->time_base;
    }
    std::cout<<" before====================== "<<std::endl;
    std::cout<<"pts:"<<packet->pts<<std::endl;
    std::cout<<"dts:"<<packet->dts<<std::endl;
    std::cout<<"duration:"<<packet->duration<<std::endl;
    std::cout<<" ====================== "<<std::endl;
    packet->pts = av_rescale_q(packet->pts,srcTimeBase,dstTimeBase);
    packet->dts = av_rescale_q(packet->dts,srcTimeBase,dstTimeBase);
    packet->duration = av_rescale_q(packet->duration,srcTimeBase,dstTimeBase);
    std::cout<<" after====================== "<<std::endl;
    std::cout<<"pts:"<<packet->pts<<std::endl;
    std::cout<<"dts:"<<packet->dts<<std::endl;
    std::cout<<"duration:"<<packet->duration<<std::endl;
    std::cout<<" ====================== "<<std::endl;

    if(packet->pts == AV_NOPTS_VALUE || packet->dts == AV_NOPTS_VALUE){
        std::cerr<<"AV_NOPTS_VALUE"<<std::endl;
        return 0;
    }
    int ret = av_interleaved_write_frame(fmtCtx,packet);
    if(ret < 0){
        std::cerr << "Mux Fail !" << std::endl;
        printError(ret);
        return -1;
    }

    return 0;
}

void FFMuxer::writeHeader()
{
    while(!readyFlag.load(std::memory_order_acquire)){
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        continue;
    }
    std::lock_guard<std::mutex>lock(mutex);
    if(headerFlag.load(std::memory_order_acquire)){
        return;
    }
    headerFlag.store(true,std::memory_order_release);

    int ret = avformat_write_header(fmtCtx,nullptr);
    if(ret < 0){
        std::cerr << "Write Header Fail !" << std::endl;
        printError(ret);
        return;
    }
}

void FFMuxer::writeTrailer()
{
    std::lock_guard<std::mutex>lock(mutex);
    if(trailerFlag.load(std::memory_order_acquire)){
        return;
    }
    trailerFlag.store(true,std::memory_order_release);

    int ret = av_write_trailer(fmtCtx);
    if(ret < 0){
        std::cerr << "Write Trailer Fail !" << std::endl;
        printError(ret);
        return;
    }
}

int FFMuxer::getAStreamIndex()
{
    return aStreamIndex;
}

int FFMuxer::getVStreamIndex()
{
    return vStreamIndex;
}
